<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// $Id: RBAC.class.php 2947 2012-05-13 15:57:48Z liu21st@gmail.com $

/**
 +------------------------------------------------------------------------------
 * 基于角色的数据库方式验证类
 +------------------------------------------------------------------------------
 * @category   ORG
 * @package  ORG
 * @subpackage  Util
 * @author    liu21st <liu21st@gmail.com>
 * @version   $Id: RBAC.class.php 2947 2012-05-13 15:57:48Z liu21st@gmail.com $
 +------------------------------------------------------------------------------
 */
// 配置文件增加设置
// USER_AUTH_ON 是否需要认证
// USER_AUTH_TYPE 认证类型
// USER_AUTH_KEY 认证识别号
// REQUIRE_AUTH_MODULE  需要认证模块
// NOT_AUTH_MODULE 无需认证模块
// USER_AUTH_GATEWAY 认证网关
// RBAC_DB_DSN  数据库连接DSN
// RBAC_ROLE_TABLE 角色表名称
// RBAC_USER_TABLE 用户表名称
// RBAC_ACCESS_TABLE 权限表名称
// RBAC_NODE_TABLE 节点表名称

class RBAC {
    // 认证方法
    static public function authenticate($map,$model='') {
        if(empty($model)) $model =  C('USER_AUTH_MODEL');
        //使用给定的Map进行认证
        return M($model)->where($map)->find();
    }

	
	// 登录检查
	static public function checkLogin() {
		if(!$_SESSION[C('USER_AUTH_KEY')]) {
			session_destroy();
			redirect(PHP_FILE.C('USER_AUTH_GATEWAY'));
		}      
        return true;
	}


    //检查当前操作是否需要认证
    static function checkAccess() {
        //如果项目要求认证，并且当前模块需要认证，则进行权限认证
        if( C('USER_AUTH_ON') ){
			$_module	=	array();
			$_action	=	array();
            if("" != C('REQUIRE_AUTH_MODULE')) {
                //需要认证的模块
                $_module['yes'] = explode(',',strtoupper(C('REQUIRE_AUTH_MODULE')));
            }else {
                //无需认证的模块
                $_module['no'] = explode(',',strtoupper(C('NOT_AUTH_MODULE')));
            }
            //检查当前模块是否需要认证
            if((!empty($_module['no']) && !in_array(strtoupper(MODULE_NAME),$_module['no'])) || (!empty($_module['yes']) && in_array(strtoupper(MODULE_NAME),$_module['yes']))) {
				if("" != C('REQUIRE_AUTH_ACTION')) {
					//需要认证的操作
					$_action['yes'] = explode(',',strtoupper(C('REQUIRE_AUTH_ACTION')));
				}else {
					//无需认证的操作
					$_action['no'] = explode(',',strtoupper(C('NOT_AUTH_ACTION')));
				}
				//检查当前操作是否需要认证
				if((!empty($_action['no']) && !in_array(strtoupper(ACTION_NAME),$_action['no'])) || (!empty($_action['yes']) && in_array(strtoupper(ACTION_NAME),$_action['yes']))) {
					return true;
				}else {
					return false;
				}
            }else {
                return false;
            }
        }
        return false;
    }


    //权限认证的过滤器方法
    static public function AccessDecision($appName=APP_NAME) {
        //检查是否需要认证
        if(RBAC::checkAccess()) {
            //存在认证识别号，则进行进一步的访问决策
            $accessGuid   =   md5($appName.MODULE_NAME.ACTION_NAME);
            if(empty($_SESSION[C('ADMIN_AUTH_KEY')])) {
                if(C('USER_AUTH_TYPE')==2) {
                    //加强验证和即时验证模式 更加安全 后台权限修改可以即时生效
                    //通过数据库进行访问检查
                    $accessList = RBAC::getAccessList($_SESSION[C('USER_AUTH_KEY')]);
                }else {
                    // 如果是管理员或者当前操作已经认证过，无需再次认证
                    if(@$_SESSION[$accessGuid]) {
                        return true;
                    }
                    //登录验证模式，比较登录后保存的权限访问列表
                    $accessList = RBAC::getAccessAction();
                }
				//dump($accessList);
                //判断是否为组件化模式，如果是，验证其全模块名
                $module = defined('P_MODULE_NAME')?  P_MODULE_NAME   :   MODULE_NAME;
                if(!isset($accessList[strtoupper($appName)][strtoupper($module)][strtoupper(ACTION_NAME)])) {
                    $_SESSION[$accessGuid]  =   false;
                    return false;
                }
                else {
                    $_SESSION[$accessGuid]	=	true;
                }
            }else{
                //管理员无需认证
				return true;
			}
        }
        return true;
    }
	
	//得到左侧的Menu
	static function getLeftMenu($value,$menuid,$nowId){
		
		$lang  = getLang();//dump($value);
		$menu = create_back_menu($value,$lang,$menuid,$nowId);
		return $menu;
	}
	
	//读取角色的Menu
	static public function getAccessMenu(){
		  $roleid  =  $_SESSION['roleid'];
		  $value   =  cache("_menutree".$roleid);
		  if(empty($value)){
			  $value = RBAC::saveAccessMenu($roleid);
		  }
		  return $value;
	}
	
	//存储角色的Menu
	static public function saveAccessMenu($roleid){
		$value = RBAC::getMenuList($roleid);
	    S("_menutree".$roleid,$value,60*30);
		return $value;
	}
	
	
	//读取用户认证的action
	static function getAccessAction(){
		$authId  = $_SESSION[C('USER_AUTH_KEY')];
		$value   = cache("_accessaction".$authId);
		if(empty($value)){
			$value = RBAC::saveAccessAction($authId);
		}
		return $value; 
	}
	
	//读取用户认证的action
	static function saveAccessAction($authId){
		$value = RBAC::getAccessList($authId);
		S("_accessaction".$authId,$value,60*30);
		return $value;
	}
	
	
	//得到当前操作的nodeid
	static public function getNowActionId($appName=APP_NAME){
		 $module = defined('P_MODULE_NAME')? P_MODULE_NAME : MODULE_NAME;
		 $access = RBAC::getAccessAction();
         return $access[strtoupper($appName)][strtoupper($module)][strtoupper(ACTION_NAME)];
	}
	
	
	//得到当前操作的父导航的id
	static function findNowPid($arr,$id){
		$father = $id;
		foreach($arr as $v){
			if($v['id']==$id){
				$father = $v['pid'];break;
			}
		}
		return $father;
	}
	
	/**
     * 取得当前认证角色的所有权限左侧menu
     * @param integer $roleId 角色Id
     * @access public
     */
	function getMenuList($roleid){
		$db     =  Db::getInstance(C('RBAC_DB_DSN'));	
		$table  =  array('menu'=>C('RBAC_MENU_TABLE'),'back'=>C('RBAC_BACK_TABLE'));
		$sql    =   "select node.* from ".$table['menu']." node where 1=1" ;
		
		if(empty($_SESSION[C('ADMIN_AUTH_KEY')])){
		 	 $sql .= " and node.id in (select access.menu_id from ".$table['back']." access where access.role_id=$roleid)";
		} 	
		$sql   .= " order by sort desc";
		$menu   =  $db->query($sql);	
		return $menu;
	}
	
	
	/**
     * 取得当前角色的所有权限列表
     * @param integer $roleId 角色Id
     * @access public
     */
    static public function getAccessList($authId) {
		$where  = "user.user_id='{$authId}'";
        $db     =   Db::getInstance(C('RBAC_DB_DSN'));
        $table  = array('role'=>C('RBAC_ROLE_TABLE'),'user'=>C('RBAC_USER_TABLE'),'access'=>C('RBAC_ACCESS_TABLE'),'node'=>C('RBAC_NODE_TABLE'));
        $sql    =   "select node.id,node.name from ".
                     $table['node']." as node ".
                    "where  node.level=0 and node.pid=0";
        $apps =   $db->query($sql);
        $access =  array();
        foreach($apps as $key=>$app) {
            $appId	 =	$app['id'];
            $appName =	$app['name'];
            // 读取项目的模块权限
            $access[strtoupper($appName)]   =  array();
            $sql    =   "select node.id,node.name from ".
                    $table['role']." as role,".
                    $table['user']." as user,".
                    $table['access']." as access ,".
                    $table['node']." as node ".
                    "where $where and user.role_id=role.id and access.role_id=role.id and role.status=1 and access.node_id=node.id and node.level=1 ";
            $modules =   $db->query($sql);
           
            // 依次读取模块的操作权限
            foreach($modules as $key=>$module) {
                $moduleId	 = $module['id'];
                $moduleName  = $module['name'];
                $sql         =   "select node.id,node.name from ".
                    $table['role']." as role,".
                    $table['user']." as user,".
                    $table['access']." as access ,".
                    $table['node']." as node ".
                    "where $where and user.role_id=role.id and access.role_id=role.id and role.status=1 and access.node_id=node.id and node.level=2 and node.pid={$moduleId}";
                $rs =   $db->query($sql);
                $action = array();
                foreach ($rs as $a){
                    $action[$a['name']]	 =	 $a['id'];
                }
                // 和公共模块的操作权限合并
				$oldModules  = @$access[strtoupper($appName)][strtoupper($moduleName)];
				if(is_array($oldModules))
					$action += $oldModules;
                $access[strtoupper($appName)][strtoupper($moduleName)]   =  array_change_key_case($action,CASE_UPPER);
            }
        }
        return $access;
    }
	
}